#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @Time     : 2018/8/21 10:38
# @Author   : crabtesting@163.com
# @File     : sprintEmail.py
# -*- coding: utf-8 -*-
import jira
import os
import smtplib
import json
import re
import datetime
import time
from email.mime.text import MIMEText
from email.header import Header

def send_sprint_email(Url,jiraUrl,UserName,Password,Project,issuetype,tester,sprintName,exedir):
    todayTime = time.strftime("%Y-%m-%d")
    firstFlag = True
    while True:
        nowHour = time.strftime("%H")
        if int(nowHour) >= 21:
            print("退出执行")
            return True
        #跟踪sprint进展
        sprint = follow_sprint(exedir,sprintName)
        #读取前一次统计的结果
        allSprintIssue = read_last_sprint_issue(exedir,sprint)
        #提取当前各issue的主要信息
        allSprintIssueNew = sprint_issue_new(Url, UserName, Password, Project, issuetype, tester, sprint, allSprintIssue, todayTime)
        # csvCom = write_csv(allSprintIssueNew, exedir, sprint)
        htmlCom = write_html(allSprintIssueNew, exedir, sprint)
        #重写统计的结果
        write_new_sprint_issue(exedir, sprint, allSprintIssueNew)
        if firstFlag:
            send_email(htmlCom, sprint)
            firstFlag = False
        time.sleep(60)


def follow_sprint(exedir,sprintName):
    if os.path.exists("%s/sprint.txt" % (exedir)):
        sprintcom = open("%s/sprint.txt" % (exedir), "r", encoding='UTF-8').read().strip()
        while sprintcom[0] != "[":
            sprintcom = sprintcom[1:]
        sprintcom = json.loads(sprintcom)
        if len(sprintcom) == 6:
            sprint = "%s %s"%(sprintName,sprintcom[0])
            nowtime = time.localtime(time.time())
            if sprintcom[3] == nowtime[0] and sprintcom[4] == nowtime[1] and sprintcom[5] == nowtime[2]:
                pass
            else:
                sprintstartdays = (datetime.date(int(nowtime[0]), int(nowtime[1]), int(nowtime[2])) - datetime.date(int(sprintcom[3]), int(sprintcom[4]), int(sprintcom[5]))).days + sprintcom[1] - 1
                sprintcom[0] = sprintcom[0] + sprintstartdays//sprintcom[2]
                sprintcom[1] = sprintstartdays%sprintcom[2] + 1
                sprintcom[3] = nowtime[0]
                sprintcom[4] = nowtime[1]
                sprintcom[5] = nowtime[2]
                sprint = "Sprint for Cadence %s" % (sprintcom[0])
                sprintwrite = open("%s/sprint.txt" % (exedir), "w", encoding='UTF-8')
                sprintcom = json.dumps(sprintcom, ensure_ascii=False)
                # print(sprintcom)
                sprintwrite.write(sprintcom)
                sprintwrite.close()
            # print(sprint)
            return sprint
        else:
            raise("请配置 /sprint/sprint.txt文件，文件内容 [当前sprint名称,sprint开始的天数,每个sprint的天数,当前的年,月,日]")
    else:
        raise("请配置 /sprint/sprint.txt文件，文件内容 [当前sprint名称,sprint开始的天数,每个sprint的天数,当前的年,月,日]")

def read_last_sprint_issue(exedir,sprint):
    if os.path.exists("%s/%s.txt" % (exedir,sprint)):
        allSprintIssue = open("%s/%s.txt" % (exedir,sprint), "r", encoding='UTF-8').read().strip()
        while allSprintIssue[0] != "{":
            allSprintIssue = allSprintIssue[1:]
        allSprintIssue = json.loads(allSprintIssue)
    else:
        allSprintIssue = {}
    return allSprintIssue

def write_new_sprint_issue(exedir, sprint, allSprintIssueNew):
    fileWrite = open("%s/%s.txt" % (exedir,sprint), "w", encoding='UTF-8')
    allSprintIssue = json.dumps(allSprintIssueNew,ensure_ascii=False)
    fileWrite.write(allSprintIssue)
    fileWrite.close()
    return True

def sprint_issue_new(Url, UserName, Password, Project, issuetype, tester, sprint, allSprintIssue, todayTime):
    jac = jira.JIRA(Url, basic_auth=(UserName, Password))
    # print("project IN (%s) AND issuetype in (%s) AND Sprint = \"%s\" ORDER BY due ASC"%(str(Project)[1:-1], str(issuetype)[1:-1], sprint))
    jiraNameArr = jac.search_issues(jql_str="project IN (%s) AND issuetype in (%s) AND Sprint = \"%s\" ORDER BY due ASC"%(str(Project)[1:-1], str(issuetype)[1:-1], sprint), maxResults=None)
    # 提取各issue的主要信息
    allSprintIssueNew = {}
    for jiraName in jiraNameArr:
        jiraName = str(jiraName)
        alldict = jac.issue(jiraName).fields.__dict__
        # 如果没有转测时间，跳过
        remarkNew = ""
        if not alldict['duedate']:
            if jiraName not in allSprintIssue:
                #如果没有转测时间，直接跳过，不做记录
                continue
            else:
                alldict['duedate'] = allSprintIssue[jiraName]["转测时间"]
                remarkNew += "%s:转测时间遗失，暂时使用曾经的转测时间，请及时检查！\n" % (todayTime)
        jiraStatus = alldict['status'].name
        assignee = alldict['assignee'].displayName
        # 判断jiraName是不是第一次出现（即：上次统计的信息中没有该数据）
        if jiraName not in allSprintIssue:
            allSprintIssueNew[jiraName] = {}
            allSprintIssueNew[jiraName]["jira"] = jiraName
            allSprintIssueNew[jiraName]["描述"] = alldict['summary']
            allSprintIssueNew[jiraName]["状态"] = jiraStatus
            allSprintIssueNew[jiraName]["转测时间"] = alldict['duedate']
            assignee = alldict['assignee'].displayName
            if assignee in tester:
                allSprintIssueNew[jiraName]["开发人员"] = ""
                allSprintIssueNew[jiraName]["测试人员"] = assignee
            else:
                allSprintIssueNew[jiraName]["开发人员"] = assignee
                allSprintIssueNew[jiraName]["测试人员"] = ""
            allSprintIssueNew[jiraName]["是否转测"] = ""
            allSprintIssueNew[jiraName]["待解决Bug"] = ""
            allSprintIssueNew[jiraName]["测试状态"] = ""
            allSprintIssueNew[jiraName]["备注"] = ""
            allSprintIssueNew[jiraName]["sprintMoveFlag"] = False
        else:
            allSprintIssueNew[jiraName] = allSprintIssue[jiraName]
            #统计一个删一个，方便统计未出现的jira（可能是移到下一个sprint了）
            del allSprintIssue[jiraName]
            allSprintIssueNew[jiraName]["状态"] = jiraStatus
            if "测试完成" not in allSprintIssueNew[jiraName]["测试状态"] or allSprintIssueNew[jiraName]["状态"] != "关闭":
                #如果转测时间和上一次统计结果不一样，要更新相关信息，并在备注中记录变化
                if allSprintIssueNew[jiraName]["转测时间"] != alldict['duedate']:
                    remarkNew += "%s:转测时间从%s变更为%s\n" % (todayTime, allSprintIssueNew[jiraName]["转测时间"], alldict['duedate'])
                    allSprintIssueNew[jiraName]["转测时间"] = alldict['duedate']
                #记录当前经办人的变化（以开发和测试分类，如果中途开发人员变化或者测试人员变化，要更新相关信息，并在备注中记录变化）
                if assignee in tester:
                    if allSprintIssueNew[jiraName]["测试人员"] == "":
                        allSprintIssueNew[jiraName]["测试人员"] = assignee
                    else:
                        if assignee != allSprintIssueNew[jiraName]["测试人员"]:
                            remarkNew += "%s:测试人员%s 变更为 %s\n" % (todayTime, allSprintIssueNew[jiraName]["测试人员"], assignee)
                            allSprintIssueNew[jiraName]["测试人员"] = assignee
                else:
                    if allSprintIssueNew[jiraName]["开发人员"] == "":
                        allSprintIssueNew[jiraName]["开发人员"] = assignee
                    else:
                        if assignee != allSprintIssueNew[jiraName]["开发人员"]:
                            remarkNew += "%s:开发人员%s 变更为 %s\n" % (todayTime, allSprintIssueNew[jiraName]["开发人员"], assignee)
                            allSprintIssueNew[jiraName]["开发人员"] = assignee
                # 筛选选备注信息（“提测”和“测试通过”）
                comments = alldict["comment"].__dict__["comments"]
                testSuccess = False
                testCommit = False
                testAuthor = ""
                for comment in comments:
                    comment = comment.__dict__
                    if "测试通过" in comment["body"] or "测试完成" in comment["body"]:
                        testSuccess = "【%s】测试通过（%s） -- 测试人员：%s" % (jiraName, comment["created"], comment["author"])
                        allSprintIssueNew[jiraName]["测试人员"] = "%s"%(comment["author"])
                        testAuthor = allSprintIssueNew[jiraName]["测试人员"]
                    elif "提测" in comment["body"]:
                        testCommit = "【%s】提测（%s） -- 开发人员：%s" % (jiraName, comment["created"], comment["author"])
                        allSprintIssueNew[jiraName]["开发人员"] = "%s"%(comment["author"])
                # 通过jira当前状态判断：
                #     关闭 ———— 则表明“测试完成”
                #     待验证 + 当前经办人是测试人员 ———— 表明“已转测”，并计算出是提前转测还是延后转测
                #     其他 ———— 表明“没有转测”，判断有没有 进度延迟
                allSprintIssueNew[jiraName]["测试状态"] = ""
                datef = re.compile("(\d+)-(\d+)-(\d+)").findall(allSprintIssueNew[jiraName]["转测时间"])[0]
                testday = (datetime.date.today() - datetime.date(int(datef[0]), int(datef[1]), int(datef[2]))).days
                if jiraStatus == "关闭":
                    #可能转测的当天就测试完成了，增加一个判断
                    if "是" not in allSprintIssueNew[jiraName]["是否转测"] and testCommit:
                        send_testStatus(testCommit,jiraName,jiraUrl,allSprintIssueNew[jiraName]["开发人员"])
                        # allSprintIssueNew[jiraName]["测试人员"] = testAuthor
                        allSprintIssueNew[jiraName]["是否转测"] = "是"
                        if testday <= 0:
                            allSprintIssueNew[jiraName]["是否转测"] += "(提前%s天转测)" % (1 - testday)
                        elif testday > 1:
                            allSprintIssueNew[jiraName]["是否转测"] += "(延迟%s天转测)" % (testday - 1)
                    if "测试完成" not in allSprintIssueNew[jiraName]["测试状态"]:
                        if testSuccess:
                            send_testStatus(testSuccess,jiraName,jiraUrl,allSprintIssueNew[jiraName]["测试人员"])
                        allSprintIssueNew[jiraName]["测试状态"] = "测试完成"
                elif jiraStatus == "待验证" and assignee in tester:
                    allSprintIssueNew[jiraName]["测试状态"] = "进行中"
                    if "是" not in allSprintIssueNew[jiraName]["是否转测"]:
                        if testCommit:
                            send_testStatus(testCommit,jiraName,jiraUrl,allSprintIssueNew[jiraName]["开发人员"])
                        allSprintIssueNew[jiraName]["是否转测"] = "是"
                        if testday <= 0:
                            allSprintIssueNew[jiraName]["是否转测"] += "(提前%s天转测)" % (1 - testday)
                        elif testday > 1:
                            allSprintIssueNew[jiraName]["是否转测"] += "(延迟%s天转测)" % (testday - 1)
                else:
                    if testday == 0:
                        allSprintIssueNew[jiraName]["是否转测"] = "否(今天如果不能开发并内测完成，请报风险)"
                    elif testday > 0:
                        allSprintIssueNew[jiraName]["是否转测"] = "否(进度延迟，请尽快开发)"
                    else:
                        allSprintIssueNew[jiraName]["是否转测"] = ""
                # 如果这个值为True，说明之前被移出了当前sprint
                if allSprintIssueNew[jiraName]["sprintMoveFlag"] == True:
                    remarkNew += "%s:sprint移回%s\n" % (todayTime, sprint)
                    allSprintIssueNew[jiraName]["sprintMoveFlag"] == False
                    allSprintIssueNew[jiraName]["sprintMoveFlag"] = False
                # 汇总当日所有备注信息
                allSprintIssueNew[jiraName]["备注"] += remarkNew
            #通过jira关联的相关内容，判断所有bug是否关闭、测试case是否关闭，如果没有关闭，提醒
            allSprintIssueNew[jiraName]["待解决Bug"] = ""
            for issuelink in alldict['issuelinks']:
                issuelinkCom = issuelink.__dict__
                try:
                    inwardIssue = issuelinkCom['raw']['inwardIssue']
                except:
                    inwardIssue = issuelinkCom['raw']['outwardIssue']
                if inwardIssue['fields']["issuetype"]["name"] == "测试":
                    if inwardIssue['fields']["status"]["name"] != "关闭" and "测试完成" in allSprintIssueNew[jiraName]["测试状态"]:
                        allSprintIssueNew[jiraName]["测试状态"] = "测试完成(请测试人员及时更新对应的测试Case状态)"
                elif inwardIssue['fields']["issuetype"]["name"] == "Bug":
                    if inwardIssue['fields']["status"]["name"] not in ["关闭", "待验证"]:
                        allSprintIssueNew[jiraName]["待解决Bug"] += "%s\n" % (inwardIssue["key"])

    # 上次记录到的jira，这次却未出现，判断是不是因为对应的sprint有变动,如果是刚刚发生变化，更新备注信息
    if allSprintIssue != {}:
        for jiraName in allSprintIssue:
            allSprintIssueNew[jiraName] = allSprintIssue[jiraName]
            if allSprintIssueNew[jiraName]["sprintMoveFlag"] == False:
                # 查看此jira被移到了哪一个sprint
                alldict = jac.issue(jiraName).fields.__dict__
                sprintNow = re.search("name=(%s \d+)" % (sprintName), alldict["customfield_10104"][-1]).group(1)
                allSprintIssueNew[jiraName]["备注"] += "%s:%s移到%s" % (todayTime, sprint, sprintNow)
                allSprintIssueNew[jiraName]["sprintMoveFlag"] = True
    return allSprintIssueNew

def write_csv(allSprintIssueNew,exedir, sprint):
    writeCom = "jira,描述,状态,转测时间,开发人员,是否转测,测试人员,测试状态,待解决bug,备注"
    for jiraName in allSprintIssueNew:
        writeCom += "\n\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\",\"%s\n\"" % \
                    (allSprintIssueNew[jiraName]["jira"], allSprintIssueNew[jiraName]["描述"],
                     allSprintIssueNew[jiraName]["状态"], allSprintIssueNew[jiraName]["转测时间"],
                     allSprintIssueNew[jiraName]["开发人员"], allSprintIssueNew[jiraName]["是否转测"],
                     allSprintIssueNew[jiraName]["测试人员"], allSprintIssueNew[jiraName]["测试状态"],
                     allSprintIssueNew[jiraName]["待解决Bug"], allSprintIssueNew[jiraName]["备注"])
    csvWrite = open("%s/%s.csv" % (exedir, sprint), "w", encoding="ANSI")
    csvWrite.write(writeCom)
    csvWrite.close()
    return writeCom

def write_html(allSprintIssueNew,exedir, sprint):
    jira = "<td width=\"10%\">jira</td>"
    描述 = "<td width=\"20%\">描述</td>"
    状态 = "<td width=\"6%\">状态</td>"
    转测时间 = "<td width=\"6%\">转测时间</td>"
    开发人员 = "<td width=\"11%\">开发人员</td>"
    是否转测 = "<td width=\"10%\">是否转测</td>"
    测试人员 = "<td width=\"11%\">测试人员</td>"
    测试状态 = "<td width=\"6%\">测试状态</td>"
    待解决Bug = "<td width=\"10%\">待解决bug</td>"
    备注 = "<td width=\"10%\">备注</tr>"
    # htmlCom = "<html><body><table cellspacing=\"0\" border=\"1\" style=\"font-size:14px\"><tr bgcolor=\"blue\">%s%s%s%s%s%s%s%s%s%s</tr>"%(jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
    muneHtml = "<tr bgcolor=\"blue\">%s%s%s%s%s%s%s%s%s%s</tr>"%(jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
    sprintMove = ""
    testSuccess =""
    testingHtml = ""
    otherHtml = ""
    for jiraName in allSprintIssueNew:
        jira = "<td><a href=\"%s%s\">%s</a></td>" % (
        jiraUrl, allSprintIssueNew[jiraName]["jira"], allSprintIssueNew[jiraName]["jira"])
        描述 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["描述"])
        状态 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["状态"])
        转测时间 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["转测时间"])
        开发人员 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["开发人员"])
        测试人员 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["测试人员"])
        备注 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["备注"])
        if allSprintIssueNew[jiraName]["待解决Bug"] != "":
            bugs = allSprintIssueNew[jiraName]["待解决Bug"].split("\n")
            BUGS = []
            for bug in bugs:
                BUGS.append("<a href=\"%s%s\"><font color=\"red\">%s</font></a>" % (jiraUrl, bug, bug))
            待解决Bug = "<td>%s</td>" % ("<br>".join(BUGS))
        else:
            待解决Bug = "<td></td>"
        if allSprintIssueNew[jiraName]["sprintMoveFlag"] == True:
            是否转测 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["是否转测"])
            测试状态 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
            # htmlCom += "<tr bgcolor=\"#BEBEBE\">%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
            sprintMove += "<tr bgcolor=\"#BEBEBE\">%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
        else:
            if "否(进度延迟，请尽快开发)" in allSprintIssueNew[jiraName]["是否转测"]:
                是否转测 = "<td bgcolor=\"red\">%s</td>" % (allSprintIssueNew[jiraName]["是否转测"])
            elif "否(今天如果不能开发并内测完成，请报风险)" in allSprintIssueNew[jiraName]["是否转测"]:
                是否转测 = "<td><font color=\"red\">%s</font></td>" % (allSprintIssueNew[jiraName]["是否转测"])
            else:
                是否转测 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["是否转测"])
            if "测试完成" in allSprintIssueNew[jiraName]["测试状态"]:
                # 测试状态 = "<td bgcolor=\"green\">%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
                测试状态 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
                testSuccess += "<tr bgcolor=\"#93FF93\">%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
                # continue
            elif "进行中" in allSprintIssueNew[jiraName]["测试状态"]:
                # 测试状态 = "<td bgcolor=\"yellow\">%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
                测试状态 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
                testingHtml += "<tr bgcolor=\"#FFFFAA\">%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
            else:
                测试状态 = "<td>%s</td>" % (allSprintIssueNew[jiraName]["测试状态"])
                # htmlCom += "<tr>%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
                otherHtml += "<tr>%s%s%s%s%s%s%s%s%s%s</tr>" % (jira, 描述, 状态, 转测时间, 开发人员, 是否转测, 测试人员, 测试状态, 待解决Bug, 备注)
    # htmlCom += "</table></body></html>"
    htmlCom = "<html><body><table cellspacing=\"0\" border=\"1\" style=\"font-size:14px\">%s%s%s%s%s</table></body></html>"%(muneHtml,otherHtml,testingHtml,testSuccess,sprintMove)
    htmlCom = htmlCom.replace("\n", "<br>")
    htmlWrite = open("%s/%s.html" % (exedir, sprint), "w", encoding='UTF-8')
    htmlWrite.write(htmlCom)
    htmlWrite.close()
    return htmlCom

def send_email(htmlCom,sprint):
    sender = 'crabtesting@163.com'
    receivers = ["crabtesting@163.com","crab1testing@163.com","crab2testing@163.com"]
    allassigneeName = "crab项目全体人员"
    try:
        smtpObj = smtplib.SMTP()
        message = MIMEText(htmlCom, 'html', 'utf-8')
        message['From'] = Header("crab", 'utf-8')
        message['To'] = Header(allassigneeName, 'utf-8')
        message['Subject'] = Header("%s 任务工作量评估和转测时间填报" % (sprint), 'utf-8')
        smtpObj.sendmail(sender, receivers, message.as_string())
        print("%s 邮件发送成功"%(sprint))
    except smtplib.SMTPException:
        print("Error: 无法发送邮件")

def send_testStatus(testStatus,jiraName,jiraUrl,author):
    if testStatus in testStatusArr:
        print("邮件已经发送过,拒绝此次请求")
        return True
    testStatusArr.append(testStatus)
    sender = 'crabtesting@163.com'
    receivers = ["crabtesting@163.com", "crab1testing@163.com", "crab2testing@163.com"]
    allassigneeName = "crab项目全体人员"
    htmlCom = "<html><body><a href=\"%s%s\">%s(点击查看详情)</a></body></html>"%(jiraUrl,jiraName,jiraName)
    try:
        smtpObj = smtplib.SMTP()
        message = MIMEText(htmlCom, 'html', 'utf-8')
        # print(author)
        message['From'] = Header("%s"%(author), 'utf-8')
        message['To'] = Header(allassigneeName, 'utf-8')
        message['Subject'] = Header(testStatus, 'utf-8')
        smtpObj.sendmail(sender, receivers, message.as_string())
        print("%s 邮件发送成功"%(testStatus))
    except smtplib.SMTPException:
        print("Error: 无法发送邮件")

if __name__ == '__main__':
    testStatusArr = []
    Url = "http://jira.com.cn"
    jiraUrl = "http://jira.com.cn/browse/"
    UserName = "crab"
    Password = "Password@crab"
    Project = ["crab项目"]
    issuetype = ["Story", "任务", "变更"]
    tester = ["测试人员crab1", "测试人员crab2","测试人员crab3"]
    sprintName = "Sprint for number"
    exedir = "%s/sprint" % (os.path.dirname(os.path.realpath(__file__)))
    send_sprint_email(Url, jiraUrl, UserName, Password, Project, issuetype, tester, sprintName, exedir)